
package com.jf.cloud.common.feign;

import cn.hutool.core.util.StrUtil;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.util.Json;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.http.HttpHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.TreeMap;

/**
 * @author zz
 * @date 2020/11/27
 */
@Component
@ConditionalOnClass({RequestInterceptor.class})
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(FeignBasicAuthRequestInterceptor.class);

    @Autowired
    private FeignInsideAuthConfig feignInsideAuthConfig;

    @Value("${spring.application.name}")
    private String appName;

    /**
     * 签名，通过签名机制，防止应用的请求参数被非法篡改，业务系统必须保证该值不被泄露。
     */
    public static final String SIGN = "sign";
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String JSON = "application/json";

    @Override
    public void apply(RequestTemplate template) {
        String url = template.url();
        if (!url.startsWith(FeignInsideAuthConfig.FEIGN_URL_PREFIX)) {
            throw new LuckException("内部请求请使用 /feign 开头 ");
        }
        // feign的内部请求，往请求头放入key 和 secret进行校验
        template.header(feignInsideAuthConfig.getKey(), feignInsideAuthConfig.getSecret());

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        String authorization = request.getHeader("Authorization");


        if (StrUtil.isNotBlank(authorization)) {
            template.header("Authorization", authorization);
        }
        String grantType = request.getHeader("grantType");
        setFeignHeader(template, request, grantType);

        template.header(Constant.LOCALE, LocaleContextHolder.getLocale().toString());
        // 请求来源为了确定异常哪个接口过来的
        template.header(HttpHeaders.REFERER, appName + request.getRequestURI());
    }

    private void setFeignHeader(RequestTemplate template, HttpServletRequest request, String grantType) {
        if (StrUtil.isNotBlank(grantType) && Objects.equals(grantType, SIGN)) {
            // 签名验签的feign，把需要的信息放在请求头
            template.header("grantType", SIGN);
            if (Objects.equals(request.getHeader(CONTENT_TYPE), JSON)) {
                logger.info("feign的post/put/delete请求");
                try {
                    String body = getBody(request);
                    TreeMap<String, Object> paramMap = Json.parseObject(body, TreeMap.class);
                    template.header("sysType", (String) paramMap.get("sysType"));
                    template.header("sysUserId", (String) paramMap.get("sysUserId"));
                    template.header("shopId", (String) paramMap.get("shopId"));
                    template.header("userId", (String) paramMap.get("userId"));
                    template.header("supplierId", (String) paramMap.get("supplierId"));
                    template.header("stationId", (String) paramMap.get("stationId"));
                } catch (IOException e) {
                    logger.error("feign签名验签出错", e);
                }
            } else {
                logger.info("feign的get请求");
                template.header("sysType", Objects.isNull(request.getParameter("sysType")) ? request.getHeader("sysType") : request.getParameter("sysType"));
                template.header("sysUserId", Objects.isNull(request.getParameter("sysUserId")) ? request.getHeader("sysUserId") : request.getParameter("sysUserId"));
                template.header("shopId", Objects.isNull(request.getParameter("shopId")) ? request.getHeader("shopId") : request.getParameter("shopId"));
                template.header("userId", Objects.isNull(request.getParameter("userId")) ? request.getHeader("userId") : request.getParameter("userId"));
                template.header("supplierId", Objects.isNull(request.getParameter("supplierId")) ? request.getHeader("supplierId") : request.getParameter("supplierId"));
                template.header("stationId", Objects.isNull(request.getParameter("stationId")) ? request.getHeader("stationId") : request.getParameter("stationId"));
            }
        }
    }

    public String getBody(HttpServletRequest request) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[1024];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } finally {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        }
        return stringBuilder.toString();
    }
}
